
import data.DataHandler;
import http.HTTPserver;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.ServerSocket;
import sip.VoIPserver;

/**
 * Main Class
 *
 * @author lailiaidi
 */
public class SIPSpeaker {

    private int obIntHTTPPort, obIntSIPPort;
    private String obStrHTTPAddress, obStrSIPAddress, obStrSIPUser;
    private final static int BUFFER = 1446;
    private boolean isListening;
    private DataHandler obDataHandler;

    /*
     * Handling the command Line param, if the param is set we take that, if it
     * is not available/not valid then: 1. take value from configuration 2. take
     * value from default hardcode default, if value in configuration is not
     * available The command Line param are: java SIPSpeaker [-c
     * config_file_name] [-user sip_uri] [-http http_bind_address] [-user
     * sip_uri] --> robot@127.0.0.1:5064 -http http_bind_address] -->
     * 127.0.0.1:8080 OR 8080 OR address/name
     */
    public static void main(String args[]) {
        int obIntHTTPPort = -1;
        int obIntSIPPort = -1;
        String obStrConfigPath = null;
        String obStrHTTPAddress = null;
        String obStrSIPAddress = null;
        String obStrSIPUser = null;

        int i = 0;
        while (i < args.length) {
            if (args[i].equals("-c")) {
                obStrConfigPath = args[i + 1];
                i++;
            } else if (args[i].equals("-user")) {
                if (args[i + 1].contains(":")) {
                    /* if the sip port is given in the command */
                    try {
                        obStrSIPUser = args[i + 1].substring(0, args[i + 1].indexOf("@"));
                    } catch (Exception e) {
                        obStrSIPAddress = null;
                        System.err.println("SIP User parameter in COMMAND LINE is not defined or not valid");
                    }
                    try {
                        InetAddress address = InetAddress.getByName(args[i + 1].substring(args[i + 1].indexOf("@") + 1,
                                args[i + 1].indexOf(":")));
                        obStrSIPAddress = address.getHostAddress();
                    } catch (Exception e) {
                        obStrSIPAddress = null;
                        System.err.println("SIP Address parameter in COMMAND LINE is not defined or not valid");
                    }
                    try {
                        obIntSIPPort = Integer.parseInt(args[i + 1].substring(
                                args[i + 1].indexOf(":") + 1, args[i + 1].length()));
                    } catch (Exception e) {
                        obStrSIPAddress = null;
                        System.err.println("SIP Port parameter in COMMAND LINE is not defined or not valid");
                    }
                } else {
                    try {
                        InetAddress address = InetAddress.getByName(args[i + 1].substring(args[i + 1].indexOf("@") + 1,
                                args[i + 1].indexOf(":")));
                        obStrSIPAddress = address.getHostAddress();
                    } catch (Exception e) {
                        obStrSIPAddress = null;
                        System.err.println("SIP Address parameter in COMMAND LINE is not defined or not valid");
                    }
                }
                i++;
            } else if (args[i].equals("-http")) {
                if (args[i + 1].contains(":")) {
                    /* if the http port is given in the command */
                    try {
                        InetAddress address = InetAddress.getByName(args[i + 1].substring(0, args[i + 1].indexOf(":")));
                        obStrHTTPAddress = address.getHostAddress();
                    } catch (Exception e) {
                        obStrHTTPAddress = null;
                        System.err.println("HTTP Address parameter in COMMAND LINE is not defined or not valid");
                    }
                    try {
                        obIntHTTPPort = Integer.parseInt(args[i + 1].substring(
                                args[i + 1].indexOf(":") + 1, args[i + 1].length()));
                    } catch (Exception e) {
                        obIntHTTPPort = -1;
                        System.err.println("HTTP Port parameter in COMMAND LINE is not defined or not valid");
                    }
                } else {
                    try {
                        /* if only http port is given in the command */
                        obIntHTTPPort = Integer.parseInt(args[i + 1].substring(
                                0, args[i + 1].length()));
                    } catch (Exception e) {
                        try {
                            /* if only http address is given in the command */
                            InetAddress address = InetAddress.getByName(args[i + 1].substring(0,
                                    args[i + 1].indexOf(":")));
                            obStrHTTPAddress = address.getHostAddress();
                        } catch (Exception ex) {
                            obStrHTTPAddress = null;
                            System.err.println("HTTP Address parameter in COMMAND LINE is not defined or not valid");
                        }
                    }
                }
            }
            i++;
        }
        obServerManager = new SIPSpeaker(obStrConfigPath, obIntHTTPPort,
                obIntSIPPort, obStrHTTPAddress, obStrSIPAddress, obStrSIPUser);
    }
    static SIPSpeaker obServerManager;

    public SIPSpeaker(String obStrConfigPath, int obIntHTTPPort,
            int obIntSIPPort, String obStrHTTPAddress, String obStrSIPAddress,
            String obStrSIPUser) {
        this.obDataHandler = new DataHandler(obStrConfigPath);
        this.isListening = true;

        if (obStrHTTPAddress == null || obStrHTTPAddress.length() <= 0) {
            this.obStrHTTPAddress = obDataHandler.getConfiguration().getHTTPHost();
        } else {
            this.obStrHTTPAddress = obStrHTTPAddress;
        }
        if (obStrSIPAddress == null || obStrSIPAddress.length() <= 0) {
            this.obStrSIPAddress = obDataHandler.getConfiguration().getSIPHost();
        } else {
            this.obStrSIPAddress = obStrSIPAddress;
        }

        if (obIntHTTPPort > 0) {
            this.obIntHTTPPort = obIntHTTPPort;
        } else {
            this.obIntHTTPPort = obDataHandler.getConfiguration().getHTTPPort();
        }

        if (obIntSIPPort > 0) {
            this.obIntSIPPort = obIntSIPPort;
        } else {
            this.obIntSIPPort = obDataHandler.getConfiguration().getSIPPort();
        }

        if (obStrSIPUser == null || obStrSIPUser.length() <= 0) {
            this.obStrSIPUser = obDataHandler.getConfiguration().getSIPUser();
        } else {
            this.obStrSIPUser = obStrSIPUser;
        }

        /* HTTP Server and VoIP Server are ready now */
        System.err.println("===");
        System.err.println("The Server is run on this settings:");
        System.err.println("obStrHTTPAddress " + this.obStrHTTPAddress);
        System.err.println("obStrSIPAddress " + this.obStrSIPAddress);
        System.err.println("obIntHTTPPort " + this.obIntHTTPPort);
        System.err.println("obIntSIPPort " + this.obIntSIPPort);
         System.err.println("obStrSIPUser " + this.obStrSIPUser);
        startServer();
    }
    private ServerSocket obHTTPManager;
    private DatagramSocket obSIPManager;
    private DatagramPacket inpacket;

    private void startServer() {
        try {
            /* Declare Object for HTTPserver */
            obHTTPManager = new ServerSocket(obIntHTTPPort, 10, InetAddress.getByName(obStrHTTPAddress));
            /* Declare Objects for VoIPserver */
            byte[] buffer = new byte[BUFFER];
            obSIPManager = new DatagramSocket(obIntSIPPort, InetAddress.getByName(obStrSIPAddress));
            inpacket = new DatagramPacket(buffer, buffer.length);

            Thread obHTTPStart = new Thread() {

                @Override
                public void run() {
                    System.out.println("INFO: TCPServer Waiting for client on "
                            + obHTTPManager.getLocalSocketAddress());
                    try {
                        while (isListening) {
                            try {
                                // CHECK IN HERE FOR MULTIPLE CONNECTION
                                HTTPserver obHTTPServer = new HTTPserver(
                                        obHTTPManager.accept(),
                                        obStrHTTPAddress, obIntHTTPPort,
                                        obDataHandler);
                                obHTTPServer.start();
                            } catch (IOException e) {
                                System.err.println("ERROR : Unable to listen to TCP connection 1");
                            }
                        }
                        obHTTPManager.close();
                    } catch (IOException e) {
                        System.err.println("ERROR: Unable to listen to TCP connection 2");
                    }
                }
            };
            /* Start the HTTP server */
            obHTTPStart.start();

            Thread obVoIPStart = new Thread() {

                @Override
                public void run() {

                    System.out.println("INFO: UDPPServer Waiting for client on "
                            + obSIPManager.getLocalSocketAddress().toString());
                    while (isListening) {
                        try {
                            obSIPManager.receive(inpacket);
                            InetAddress obClientAddress = inpacket.getAddress();
                            int obClientPort = inpacket.getPort();
                            String input = new String(inpacket.getData(), 0,
                                    inpacket.getLength(), "US-ASCII");
                            if (input.contains("SIP")) {
                                /*
                                 * Put this client into the table if it's not
                                 * served by server
                                 */
                                obDataHandler.addClient(obClientAddress.toString(), obClientPort);
                                VoIPserver obVoIPserver = new VoIPserver(
                                        obClientAddress, obClientPort, input,
                                        obStrSIPUser, obSIPManager,
                                        obDataHandler);
                                obVoIPserver.start();
                            }
                        } catch (UnsupportedEncodingException e1) {
                            System.err.println("ERROR: Unsopported Encoding for UDP connection");
                        } catch (IOException e) {
                            System.err.println("ERROR: Unable to listen to UDP  connection");
                        }
                    }
                    obSIPManager.close();
                }
            };

            /* Start the VoIP server */
            obVoIPStart.start();
        } catch (IOException e) {
            System.err.println("ERROR: Unable to set TCP/UDP Server connection to the Address and Port");
            System.exit(1);
        }
    }

    public DataHandler getDataHandler() {
        return obDataHandler;
    }
}
